//
// Created by tang on 23-4-23.
//

#include "meta_parser.hpp"
#include <fstream>
#include "common/namespace.h"
#include "mustache/mustache.hpp"
#include "mustache/mustache_templates.h"
#include "parser/cursor.hpp"
#include "types/class.hpp"

meta_parser::meta_parser(std::string source_path): source_path_(std::move(source_path)) {
    source_filter_ = [this](Cursor cursor)->bool {
        return cursor.source_file() == source_path_;
    };
}

bool meta_parser::parser (const Cursor &cursor)
{
    Namespace ns;
    bool ret = build_class_ast (cursor, ns);
    ns.clear();
    return ret;
}

bool meta_parser::generate (const std::string &out_path)
{
    using namespace kainjow::mustache;

    mustache tmpl{GeneratedBinddata};
    data ctx;

    ctx.set ("header", source_path_);
    data types{data::type::list};

    for (auto &x: classes_) {
        data type;
        type.set ("type", x.second->qualified_name_);

        // fields
        data fields{data::type::list};
        for (auto &field: x.second->fields_) {
            if (field->should_compile()) {
                data field_info;
                field_info.set ("field_key", field->key());
                field_info.set ("field_name", field->name());
                fields << field_info;
            }
        }
        type.set("fields", fields);

        // methods
        data methods{data::type::list};
        for (auto &method: x.second->methods_) {
            if (method->should_compile()) {

            }
        }
        type.set("methods", methods);

        types << type;
    }

    ctx.set ("types", types);

    std::string content = tmpl.render (ctx);

    std::ofstream ofs(out_path, std::ios::out | std::ios::trunc);
    if (ofs.is_open()) {
        ofs << content;
        ofs.close();
        return true ;
    }
    printf ("file %s could not be open!\n", out_path.c_str());

    return false;
}

bool meta_parser::finish ()
{
    return false;
}

void meta_parser::prepare () {}

bool meta_parser::build_class_ast (const Cursor &cursor, Namespace &current_ns) {
    bool ok = true;
    auto children = cursor.children(source_filter_);
    for (auto &child: children)
    {
        auto kind = child.kind ();

        if (child.is_definition () && (kind == CXCursor_ClassDecl || kind == CXCursor_StructDecl))
        {
            // print class
            auto cls = new Class (child, current_ns);
            if (!cls->should_compile() || !add_codegen_class (cls)) {
                delete cls;
            }
        }
        else if (kind == CXCursor_Namespace)
        {
            auto name = child.display_name ();
            current_ns.emplace_back (name);
            ok = ok && build_class_ast (child, current_ns);
            current_ns.pop_back ();
        }
        // else
        // {
        //     printf("unknown kind %d(%s), location: %s\n", kind, child.spelling().c_str(), child.file_line().c_str());
        // }

        if (!ok)
            break;
    }

    return ok;
}
bool meta_parser::add_codegen_class (Class *cls)
{
    auto it = classes_.find (cls->qualified_name_);
    if (it == end(classes_)) {
        classes_[cls->qualified_name_] = std::shared_ptr<Class>(cls);
        return true;
    } else {
        return false;
    }
}
